package QRCG;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import javax.servlet.annotation.WebServlet;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

/**
 * Servlet implementation class CreateGameServe
 */
@WebServlet("/CreateGameServe")
public class CreateGameServe extends HttpServlet {
  private static final long serialVersionUID = 1L;

  private static final int GAME_NAME_MAX_LENGTH = 30;
  private static final int GAME_DESCRIPTION_MAX_LENGTH = 500;
  private static final int HINT_DESCRIPTION_MAX_LENGTH = 140;
  private static final int HINT_KEYWORD_MAX_LENGTH = 50;

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    if (request.getCharacterEncoding() == null) {
      request.setCharacterEncoding("UTF-8");
    }
    HttpSession session = request.getSession(true);
    String userId = (String)session.getAttribute("user_id");

    String name = request.getParameter("game_name");
    String description = request.getParameter("game_description");
    String beginDate = request.getParameter("begin_date");
    String endDate = request.getParameter("end_date");
    String location = request.getParameter("game_location");
    String activation = request.getParameter("activation");
    String gameType = request.getParameter("game_type");
    String[] hintDescriptions = request.getParameterValues("hint_descriptions");
    String[] hintKeywords = request.getParameterValues("hint_keywords");
    String[] hintCoordinates = request.getParameterValues("hint_coordinates");
    String[] hintAddresses = request.getParameterValues("hint_addresses");

    String errorMessages = "";

    boolean success = true;
    if (!Util.hasTextContent(name)) {
      errorMessages += ("Name field cannot be empty! \n");
      success = false;
    }

    if (!Util.hasTextContent(description)) {
      errorMessages += ("Description field cannot be empty! \n");
      success = false;
    }

    if (!Util.hasTextContent(beginDate)) {
      errorMessages += ("Begin date field cannot be empty! \n");
      success = false;
    }

    if (!Util.hasTextContent(endDate)) {
      errorMessages += ("End date cannot be empty! \n");
      success = false;
    }

    if (name.length() > GAME_NAME_MAX_LENGTH) {
      errorMessages += ("Game name's length cannot be longer than " + GAME_NAME_MAX_LENGTH  + "characters. \n");
      success = false;
    }

    if (description.length() > GAME_DESCRIPTION_MAX_LENGTH) {
      errorMessages += ("Game description's length cannot be longer than "
          + GAME_NAME_MAX_LENGTH  + "characters. \n");
      success = false;
    }

    Calendar calBeginDate = Calendar.getInstance();
    Calendar calEndDate = Calendar.getInstance();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    try {
      calBeginDate.setTime(sdf.parse(beginDate));
      // These exist in order to be able to create games that start today.
      calBeginDate.set(Calendar.HOUR_OF_DAY, 23);
      calBeginDate.set(Calendar.MINUTE, 59);
      calBeginDate.set(Calendar.SECOND, 59);

      calEndDate.setTime(sdf.parse(endDate));
      // These exist in order to be able to create games that end today.
      calEndDate.set(Calendar.HOUR_OF_DAY, 23);
      calEndDate.set(Calendar.MINUTE, 59);
      calEndDate.set(Calendar.SECOND, 59);

      if (calBeginDate.compareTo(calEndDate) > 0) {
        errorMessages += ("Begin date cannot come later end date! \n");
        success = false;
      }
      if (calEndDate.compareTo(Calendar.getInstance()) < 0) {
        errorMessages += ("End date cannot be earlier than today's date! \n");
        success = false;
      }
      if (calBeginDate.compareTo(Calendar.getInstance()) < 0) {
        errorMessages += ("Begin date cannot be earlier than today's date! \n");
        success = false;
      }
    } catch (ParseException e) {
      errorMessages += ("Dates should be in YYYY-MM-DD format! \n");
      success = false;
    }

    if (hintDescriptions == null || hintKeywords == null || hintCoordinates == null ||
        hintAddresses == null || hintDescriptions.length == 0 || hintKeywords.length == 0 ||
        hintCoordinates.length == 0 || hintAddresses.length == 0) {
      errorMessages += ("There should be at least one hint! \n");
      success = false;
    } else {
      int N = hintDescriptions.length;
      for (int i = 0; success && i < N; ++i) {
        if (hintDescriptions[i].length() > HINT_DESCRIPTION_MAX_LENGTH) {
          errorMessages += ("Hint descriptions cannot be longer than " +
              HINT_DESCRIPTION_MAX_LENGTH + " characters! \n");
          success = false;
        } else if (hintKeywords[i].length() > HINT_KEYWORD_MAX_LENGTH) {
          errorMessages += ("Hint keywords cannot be longer than " +
              HINT_KEYWORD_MAX_LENGTH + " characters! \n");
          success = false;
        } else if (!Util.hasTextContent(hintCoordinates[i])) {
          // TODO(cimamoglu): Check for the content format as well.
          errorMessages += ("All hint coordinates should be specified! \n");
          success = false;
        } else if (!Util.hasTextContent(hintDescriptions[i]) || !Util.hasTextContent(hintKeywords[i])) {
          errorMessages += ("All hint keywords and descriptions should be specified! \n");
          success = false;
        }
      }

      outer:
      for (int i = 0; i < N; ++i) {
        for (int j = i + 1; j < N; ++j) {
          if (hintKeywords[i].equals(hintKeywords[j])) {
            errorMessages += ("All hint keywords should be different! \n");
            success = false;
            break outer;
          }
        }
      }
    }

    String gameId = null;
    if (success) {
      DatabaseManager databaseManager = new DatabaseManager();
      Connection connection = null;
      Statement statement  = null;
      try {
        databaseManager.connect();
        connection = databaseManager.getConnection();
        // Commit all insertions at once, to keep database consistency in case of an SQL problem.
        connection.setAutoCommit(false);

        // There are as many levels as hint descriptions.
        int levelCount = hintDescriptions.length;
        String gameInsertionQuery = "INSERT INTO Games (creator_id,name,description,begin_date," +
        		"end_date,location_id,is_active,level_count,rating,game_type) "
            + "VALUES("
            + userId // No surrounding single quotes!
            + ",'"
            + name
            + "','"
            + description
            + "','"
            + beginDate
            + "','"
            + endDate
            + "',"
            + location // No surrounding single quotes!
            + ","
            + activation // No surrounding single quotes!
            + ","
            + levelCount // No surrounding single quotes!
            + ","
            + "NULL" // Initially, there is no rating. No surrounding single quotes!
            + ","
            + gameType // No surrounding single quotes!
            + ")";

        statement = connection.createStatement();;
        ResultSet resultSet = null;

        // The second parameter is for getting the game_id (the auto incremented, unique key of
        // the table).
        statement.execute(gameInsertionQuery, Statement.RETURN_GENERATED_KEYS);
        resultSet = statement.getGeneratedKeys();

        statement.clearBatch();
        if (resultSet.next()) {
          String lastGameId = resultSet.getString(1);
          gameId = lastGameId;
          //ArrayList<String> hintInsertionQueries = new ArrayList<String>();
          for (int i = 1; i <= levelCount; ++i) {
            String query = "INSERT INTO GameLevels (game_id,level_id,level_code,hint,qr_address) "
                + "VALUES("
                + lastGameId // No surrounding single quotes!
                + ","
                + i // No surrounding single quotes!
                + ",'"
                + hintKeywords[i - 1].toLowerCase() //for convention
                + "','"
                + hintDescriptions[i - 1]
                + "','"
                + hintCoordinates[i - 1]
                + "')";
            statement.addBatch(query);
          }
          statement.executeBatch();
          connection.commit();
        } else {
          throw new SQLException("Game information couldn't be created " +
          		"successfully in the database.");
        }
      } catch (SQLException e) {
        success = false;
        errorMessages += ("Transaction could not be completed, please try again! \n");
        try {
          connection.rollback();
        } catch (SQLException e1) {
          errorMessages += ("Transaction could not be completed, and there has been an" +
          		"error in rolling the changes back! \n");
        }
      } finally {
        try {
          if (connection != null) {
            connection.setAutoCommit(true);
            connection.close();
          }
          if (statement != null) {
            statement.close();
          }
        } catch (SQLException e) {
          errorMessages += ("Transaction is completed, but there is a problem" +
          		"in closing the database connection! \n");
          e.printStackTrace();
        }
      }
    }

    // If there is an error, return back to the CreateNewGame.jsp and keep the
    // values the user entered.
    RequestDispatcher rd = null;

    // Pass hint information in any case.
    request.setAttribute("hint_descriptions", hintDescriptions);
    request.setAttribute("hint_keywords", hintKeywords);
    request.setAttribute("hint_coordinates", hintCoordinates);
    request.setAttribute("hint_addresses", hintAddresses);

    if (!success) {
      rd = request.getRequestDispatcher("CreateNewGame.jsp");
      // Pass error messages
      request.setAttribute("errorMessages", errorMessages);
      // Keep user's values
      request.setAttribute("game_name", name);
      request.setAttribute("game_description", description);
      request.setAttribute("begin_date", beginDate);
      request.setAttribute("end_date", endDate);
      request.setAttribute("game_location_id", location);
      request.setAttribute("activation", activation);
      request.setAttribute("game_type", gameType);
      rd.forward(request, response);
    } else {
      request.setAttribute("game_id", gameId);
      rd = request.getRequestDispatcher("GameCreated.jsp");
      rd.forward(request, response);
    }
  }
}
